import BigNumber from "bignumber.js"

import { toWei } from '@app/utils'
import { logger } from '@pkg/logger'
import { Transactions } from '@app/storage/transaction'
import { OzTokenABI, OzTokenByteCode } from '@app/contract/oztoken'

import * as config from '@config/server.json'

class Signer {
    private trongrid: any

    constructor(trongrid: any) {
        this.trongrid = trongrid
    }

    /**
     * 转移TRX代币
     * @param privateKey - 发送者私钥
     * @param to - 接收地址
     * @param amount - 发送金额
     */
    async transfer(privateKey: string, to: string, amount: BigNumber) {
        let from: string | undefined
        let txID: string | undefined

        try {
            const sum = this.trongrid.toSun(amount.toString(10))
            from = this.trongrid.address.fromPrivateKey(privateKey) as string

            const trade = await this.trongrid.transactionBuilder.sendTrx(to, sum, from)
            txID = trade.txID as string

            await Transactions.insert(from, txID, trade)

            const signedtxn = await this.trongrid.trx.sign(trade, privateKey)
            const receipt = await this.trongrid.trx.sendRawTransaction(signedtxn)
            if (receipt.code) {
                throw new Error(receipt.code)
            }

            logger.warn('[Signer] transfer %s TRX from %s to %s, hash: %s', amount, from, to, txID)
            return receipt  
        } catch (error) {
            logger.error('[Signer] failed to transfer %s TRX from %s to %s, hash: %s, reason: %s',
                amount, from, to, txID, error)
            throw error
        }
    }

    /**
     * 部署TRC20代币
     * @param name - 代币名称
     * @param symbol - 代币符号
     * @param decimals - 小数精度
     * @param initialSupply - 初始供应量
     */
    async deployTRC20(name: string, symbol: string, decimals: number, initialSupply: BigNumber) {
        let txID: string | undefined
        let contractAddress: string | undefined

        try {
            const options = {
                name: name,
                abi: OzTokenABI,
                bytecode:OzTokenByteCode,
                userFeePercentage: 100,
                feeLimit: this.trongrid.toSun('240'),
                parameters: [name, symbol, decimals, initialSupply.toString()]
            }

            const from = this.trongrid.address.fromPrivateKey(config.hotwallet)
            const trade = await this.trongrid.transactionBuilder.createSmartContract(options, from)

            txID = trade.txID as string
            contractAddress = this.trongrid.address.fromHex(trade.contract_address)

            await Transactions.insert(from, txID, trade)

            const signedtxn = await this.trongrid.trx.sign(trade, config.hotwallet)
            const receipt = await this.trongrid.trx.sendRawTransaction(signedtxn)
            if (receipt.code) {
                throw new Error(receipt.code)
            }

            logger.warn('[Signer] deploy TRC20 token, contract: %s, hash: %s', contractAddress, txID)
            return receipt
        } catch (error) {
            logger.error('[Signer] failed to deploy TRC20 token, contract: %s, hash: %s, reason: %s',
                contractAddress, txID, error)
            throw error
        }
    }

    /**
     * 转移TRC20代币
     * @param privateKey - 发送者私钥
     * @param to - 接收地址
     * @param amount - 发送金额
     */
    async transferTRC20(contractAddress: string, privateKey: string, to: string, amount: BigNumber, decimals: number) {
        let txID: string | undefined
        const senderAddress = this.trongrid.address.fromPrivateKey(privateKey)

        try {
            const method = 'transfer(address,uint256)'
            const options = { feeLimit: this.trongrid.toSun(config.minTrxBalance) }
            const parameters = [{ type: 'address', value: to }, { type: 'uint256', value: toWei(amount, decimals) }]
    
            const trade = await this.trongrid.transactionBuilder.triggerSmartContract(
                contractAddress,
                method,
                options,
                parameters, 
                senderAddress
            )
            txID = trade.transaction.txID as string

            await Transactions.insert(senderAddress, txID, trade.transaction)

            const signedtxn = await this.trongrid.trx.sign(trade.transaction, privateKey)
            const receipt = await this.trongrid.trx.sendRawTransaction(signedtxn)
            if (receipt.code) {
                throw new Error(receipt.code)
            }

            logger.warn('[Signer] transfer %s TRC20 from %s to %s, contract: %s, hash: %s',
                amount, senderAddress, to, contractAddress, txID)
            return receipt
        } catch (error) {
            logger.error('[Signer] failed to transfer %s TRC20 from %s to %s, contract: %s, hash: %s, reason: %s',
                amount, senderAddress, to, contractAddress, txID, error)
            throw error
        }
    }
}

export { Signer }
